{/* Copyright 2020 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-aria/toast';
import statelyDocs from 'docs:@react-stately/toast';
import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, PageDescription, TypeLink} from '@react-spectrum/docs';
import packageData from '@react-aria/toast/package.json';
import Anatomy from './toast-anatomy.svg';
import ChevronRight from '@spectrum-icons/workflow/ChevronRight';
import {Keyboard} from '@react-spectrum/text';

```tsx import
import {useToastRegion} from '@react-aria/toast';
```

---
category: Status
keywords: [toast, notifications, alert, aria]
---

# useToast

<PageDescription>{docs.exports.useToast.description}</PageDescription>

<HeaderInfo
  packageData={packageData}
  componentNames={['useToastRegion', 'useToast']}
  sourceData={[
    {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/'}
  ]} />

## API

<FunctionAPI function={docs.exports.useToastRegion} links={docs.links} />
<FunctionAPI function={docs.exports.useToast} links={docs.links} />

## Features

There is no built in way to display toast notifications in HTML. <TypeLink links={docs.links} type={docs.exports.useToastRegion} /> and <TypeLink links={docs.links} type={docs.exports.useToast} /> help achieve accessible toasts that can be styled as needed.

* **Accessible** – Toasts follow the [ARIA alertdialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/). They are rendered in a [landmark region](https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/), which keyboard and screen reader users can easily jump to when an alert is announced.
* **Focus management** – When a toast unmounts, focus is moved to the next toast if any. Otherwise, focus is restored to where it was before navigating to the toast region. Tabbing through the Toast region will move from newest to oldest.

## Anatomy

<Anatomy role="img" aria-label="Toast anatomy diagram, showing the toast's title and close button within the toast region." />

A toast region is an [ARIA landmark region](https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/) labeled "Notifications" by default. A toast region contains one or more visible toasts, in chronological order. When the limit is reached, additional toasts are queued until the user dismisses one. Each toast is a non-modal ARIA [alertdialog](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/), containing the content of the notification and a close button.

Landmark regions including the toast container can be navigated using the keyboard by pressing the <Keyboard>F6</Keyboard> key to move forward, and the <Keyboard>Shift</Keyboard> + <Keyboard>F6</Keyboard> key to move backward. This provides an easy way for keyboard users to jump to the toasts from anywhere in the app. When the last toast is closed, keyboard focus is restored.

`useToastRegion` returns props that you should spread onto the toast container element:

<TypeContext.Provider value={docs.links}>
  <InterfaceType properties={docs.links[docs.exports.useToastRegion.return.id].properties} />
</TypeContext.Provider>

`useToast` returns props that you should spread onto an individual toast and its child elements:

<TypeContext.Provider value={docs.links}>
  <InterfaceType properties={docs.links[docs.exports.useToast.return.id].properties} />
</TypeContext.Provider>

## Example

Toasts consist of three components. The first is a `ToastProvider` component which will manage the state for the toast queue with the <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useToastState} /> hook. Alternatively, you could use a global toast queue ([see below](#global-toast-queue)).

```tsx
import {useToastState} from '@react-stately/toast';

function ToastProvider({children, ...props}) {
  let state = useToastState({
    maxVisibleToasts: 5
  });

  return (
    <>
      {children(state)}
      {state.visibleToasts.length > 0 && <ToastRegion {...props} state={state} />}
    </>
  );
}
```

```tsx import
// Actual implementation we use in the docs, using global queue.
function ToastProvider({children}) {
  return children(toastQueue);
}
```

The `ToastRegion` component will be rendered when there are toasts to display. It uses the <TypeLink links={docs.links} type={docs.exports.useToastRegion} /> hook to create a landmark region, allowing keyboard and screen reader users to easily navigate to it.

```tsx example export=true render=false
import type {AriaToastRegionProps} from '@react-aria/toast';
import type {ToastState} from '@react-stately/toast';
import {useToastRegion} from '@react-aria/toast';

interface ToastRegionProps<T> extends AriaToastRegionProps {
  state: ToastState<T>
}

function ToastRegion<T extends React.ReactNode>({state, ...props}: ToastRegionProps<T>) {
  let ref = React.useRef(null);
  let {regionProps} = useToastRegion(props, state, ref);

  return (
    <div {...regionProps} ref={ref} className="toast-region">
      {state.visibleToasts.map(toast => (
        <Toast key={toast.key} toast={toast} state={state} />
      ))}
    </div>
  );
}
```

Finally, we need the `Toast` component to render an individual toast within a `ToastRegion`, built with <TypeLink links={docs.links} type={docs.exports.useToast} />.

```tsx example render=false export=true
import type {AriaToastProps} from '@react-aria/toast';
import {useToast} from '@react-aria/toast';

// Reuse the Button from your component library. See below for details.
import {Button} from 'your-component-library';

interface ToastProps<T> extends AriaToastProps<T> {
  state: ToastState<T>
}

function Toast<T extends React.ReactNode>({state, ...props}: ToastProps<T>) {
  let ref = React.useRef(null);
  let {toastProps, contentProps, titleProps, closeButtonProps} = useToast(props, state, ref);

  return (
    <div {...toastProps} ref={ref} className="toast">
      <div {...contentProps}>
        <div {...titleProps}>{props.toast.content}</div>
      </div>
      <Button {...closeButtonProps}>x</Button>
    </div>
  );
}
```

```tsx example
<ToastProvider>
  {state => (
    <Button onPress={() => state.add('Toast is done!')}>Show toast</Button>
  )}
</ToastProvider>
```

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show CSS</summary>

```css
.toast-region {
  position: fixed;
  bottom: 16px;
  right: 16px;
  display: flex;
  flex-direction: column-reverse;
  gap: 8px;
}

.toast {
  display: flex;
  align-items: center;
  gap: 16px;
  background: slateblue;
  color: white;
  padding: 12px 16px;
  border-radius: 8px;
}

.toast button {
  background: none;
  border: none;
  appearance: none;
  border-radius: 50%;
  height: 32px;
  width: 32px;
  font-size: 16px;
  border: 1px solid white;
  color: white;
  padding: 0;
}

.toast button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px slateblue, 0 0 0 4px white;
}

.toast button:active {
  background: rgba(255, 255, 255, 0.2);
}
```

</details>

### Button

The `Button` component is used in the above example to close a toast. It is built using the [useButton](../Button/useButton.html) hook, and can be shared with many other components.

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>

```tsx example export=true render=false
import {useButton} from '@react-aria/button';

function Button(props) {
  let ref = React.useRef(null);
  let {buttonProps} = useButton(props, ref);
  return <button {...buttonProps} ref={ref}>{props.children}</button>;
}
```

</details>

## Usage

The following examples show how to use the `ToastProvider` component created in the above example.

### Auto-dismiss

Toasts support a `timeout` option to automatically hide them after a certain amount of time. For accessibility, toasts should have a minimum timeout of 5 seconds to give users enough time to read them. If a toast includes action buttons or other interactive elements it should not auto dismiss. In addition, timers will automatically pause when the user focuses or hovers over a toast.

Be sure only to automatically dismiss toasts when the information is not important, or may be found elsewhere. Some users may require additional time to read a toast message, and screen zoom users may miss toasts entirely.

```tsx example
<ToastProvider>
  {state => (
    ///- begin highlight -///
    <Button onPress={() => state.add('Toast still toasting!', {timeout: 5000})}>
    {/*- end highlight -*/}
      Show toast
    </Button>
  )}
</ToastProvider>
```

### Programmatic dismissal

Toasts may be programmatically dismissed if they become irrelevant before the user manually closes them. `state.add` returns a key for the toast which may be passed to `state.close` to dismiss the toast.

```tsx example
function Example() {
  let [toastKey, setToastKey] = React.useState(null);

  return (
    <ToastProvider>
      {state => (
        <Button
          onPress={() => {
            if (!toastKey) {
              ///- begin highlight -///
              setToastKey(state.add('Unable to save', {onClose: () => setToastKey(null)}));
              ///- end highlight -///
            } else {
              ///- begin highlight -///
              state.close(toastKey);
              ///- end highlight -///
            }
          }}>
          {toastKey ? 'Hide' : 'Show'} Toast
        </Button>
      )}
    </ToastProvider>
  );
}
```

## Advanced topics

### Global toast queue

In the above examples, each `ToastProvider` has a separate queue. This setup is simple, and fine for most cases where you can wrap the entire app in a single `ToastProvider`. However, in more complex situations, you may want to keep the toast queue outside the React tree so that toasts can be queued from anywhere. This can be done by creating your own <TypeLink links={statelyDocs.links} type={statelyDocs.exports.ToastQueue} /> and subscribing to it using the <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useToastQueue} /> hook rather than `useToastState`.

```tsx example export=true hidden
import {ToastQueue, useToastQueue} from '@react-stately/toast';
import {createPortal} from 'react-dom';

// Create a global toast queue.
///- begin highlight -///
const toastQueue = new ToastQueue({
  maxVisibleToasts: 5
});
///- end highlight -///

function GlobalToastRegion(props) {
  // Subscribe to it.
  ///- begin highlight -///
  let state = useToastQueue(toastQueue);
  ///- end highlight -///

  // Render toast region.
  return state.visibleToasts.length > 0
    ? createPortal(<ToastRegion {...props} state={state} />, document.body)
    : null;
}

// Render it somewhere in your app.
<GlobalToastRegion />
```

Now you can queue a toast from anywhere:

```tsx example
<Button onPress={() => toastQueue.add('Toast is done!')}>Show toast</Button>
```

### TypeScript

A `ToastQueue` and `useToastState` use a generic type to represent toast content. The examples so far have used strings, but you can type this however you want to enable passing custom objects or options. This example uses a custom object to support toasts with both a title and description.

```tsx
import type {QueuedToast} from '@react-stately/toast';

/*- begin highlight -*/
interface MyToast {
  title: string,
  description: string
}
/*- end highlight -*/

function ToastProvider() {
  /*- begin highlight -*/
  let state = useToastState<MyToast>();
  /*- end highlight -*/

  // ...
}

interface ToastProps {
  /*- begin highlight -*/
  toast: QueuedToast<MyToast>
  /*- end highlight -*/
}

function Toast(props: ToastProps) {
  // ...

  let {toastProps, titleProps, descriptionProps, closeButtonProps} = useToast(props, state, ref);

  return (
    <div {...toastProps} ref={ref} className="toast">
      <div>
        {/*- begin highlight -*/}
        <div {...titleProps}>{props.toast.content.title}</div>
        <div {...descriptionProps}>{props.toast.content.description}</div>
        {/*- end highlight -*/}
      </div>
      <Button {...closeButtonProps}>x</Button>
    </div>
  );
}

// Queuing a toast
/*- begin highlight -*/
state.add({title: 'Success!', description: 'Toast is done.'});
/*- end highlight -*/
```
